package com.springboot.cloud.gateway.service.impl;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springboot.cloud.gateway.dao.GatewayRouteMapper;
import com.springboot.cloud.gateway.entity.po.GatewayRoute;
import com.springboot.cloud.gateway.service.IRouteService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class RouteService implements IRouteService {

    private static final String GATEWAY_ROUTES = "gateway_routes::";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private GatewayRouteMapper gatewayRouteMapper;

    @CreateCache(name = GATEWAY_ROUTES, cacheType = CacheType.REMOTE)
    private Cache<String, RouteDefinition> gatewayRouteCache;

    private Map<String, RouteDefinition> routeDefinitionMaps = new HashMap<>();

    @PostConstruct
    private void loadRouteDefinition() {
        log.info("loadRouteDefinition, 开始初使化路由");
        Set<String> gatewayKeys = stringRedisTemplate.keys(GATEWAY_ROUTES + "*");
        if (!CollectionUtils.isEmpty(gatewayKeys)) {


            log.info("预计初使化路由, gatewayKeys：{}", gatewayKeys);
            // 去掉key的前缀
            Set<String> gatewayKeyIds = gatewayKeys.stream().map(key -> {
                return key.replace(GATEWAY_ROUTES, StringUtils.EMPTY);
            }).collect(Collectors.toSet());
            Map<String, RouteDefinition> allRoutes = gatewayRouteCache.getAll(gatewayKeyIds);
            log.info("gatewayKeys：{}", allRoutes);
            // 以下代码原因是，jetcache将RouteDefinition返序列化后，uri发生变化，未初使化，导致路由异常，以下代码是重新初使化uri
            allRoutes.values().forEach(routeDefinition -> {
                try {
                    routeDefinition.setUri(new URI(routeDefinition.getUri().toASCIIString()));
                } catch (URISyntaxException e) {
                    log.error("网关加载RouteDefinition异常：", e);
                }
            });
            routeDefinitionMaps.putAll(allRoutes);
        } else {
            Map<String, RouteDefinition> allRoutes = new HashMap<>();
            List<GatewayRoute> gatewayRouteLists = gatewayRouteMapper.queryAll();
            for (GatewayRoute gatewayRoute : gatewayRouteLists) {
                RouteDefinition routeDefinition = gatewayRouteToRouteDefinition(gatewayRoute);
                allRoutes.put(gatewayRoute.getRouteId(), routeDefinition);
                gatewayRouteCache.put(gatewayRoute.getRouteId(), routeDefinition);
            }
            routeDefinitionMaps.putAll(allRoutes);
        }
        log.info("共初使化路由信息：{}", routeDefinitionMaps.size());
    }

    private RouteDefinition gatewayRouteToRouteDefinition(GatewayRoute gatewayRoute) {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(gatewayRoute.getRouteId());
        routeDefinition.setOrder(gatewayRoute.getOrders());
        routeDefinition.setUri(URI.create(gatewayRoute.getUri()));
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            routeDefinition.setFilters(objectMapper.readValue(gatewayRoute.getFilters(), new TypeReference<List<FilterDefinition>>() {
            }));
            routeDefinition.setPredicates(objectMapper.readValue(gatewayRoute.getPredicates(), new TypeReference<List<PredicateDefinition>>() {
            }));
        } catch (IOException e) {
            log.error("网关路由对象转换失败", e);
        }
        return routeDefinition;
    }

    @Override
    public Collection<RouteDefinition> getRouteDefinitions() {
        return routeDefinitionMaps.values();
    }

    @Override
    public boolean save(RouteDefinition routeDefinition) {
        routeDefinitionMaps.put(routeDefinition.getId(), routeDefinition);
        log.info("新增路由1条：{},目前路由共{}条", routeDefinition, routeDefinitionMaps.size());
        return true;
    }

    @Override
    public boolean delete(String routeId) {
        routeDefinitionMaps.remove(routeId);
        log.info("删除路由1条：{},目前路由共{}条", routeId, routeDefinitionMaps.size());
        return true;
    }
}
